package com.adan.auditlog;

import com.adan.auditlog.annotation.MyBatisAuditLog;
import com.adan.auditlog.config.AuditLogContext;
import com.adan.auditlog.entity.AuditLogParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Aspect
@Component
@Slf4j
public class AuditLogAspect {
	private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
	private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
	@Autowired
	private AuditLogContext auditLogContext;

	@Around("mapper()")
	private Object auditLogAround(ProceedingJoinPoint joinPoint) throws Throwable {
		try {
			AuditLogParam.AuditLogParamBuilder paramBuilder = AuditLogParam.builder();
			paramBuilder.enableAuditLog(true);
			MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
			MyBatisAuditLog myBatisAuditLogAnno = methodSignature.getMethod().getAnnotation(MyBatisAuditLog.class);
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			// 设置操作的接口方法名
			String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
			paramBuilder.methodName(methodName);
			paramBuilder.moduleName(myBatisAuditLogAnno.value());
			// 获得日志注解上自定义的日志信息
			String logInfo = myBatisAuditLogAnno.remarkField();
			if(StringUtils.isNotBlank(logInfo)) {
				// Spel表达式解析日志信息
				// 获得方法参数名数组
				String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
				if (parameterNames != null && parameterNames.length > 0) {
					EvaluationContext context = new StandardEvaluationContext();
					//获取方法参数值
					Object[] args = joinPoint.getArgs();
					for (int i = 0; i < args.length; i++) {
						context.setVariable(parameterNames[i], args[i]); // 替换spel里的变量值为实际值， 比如 #user -->  user对象
					}
					// 解析出实际的日志信息
					Object elValue = spelExpressionParser.parseExpression(logInfo).getValue(context);
					if (Objects.nonNull(elValue)) {
						paramBuilder.remark(elValue.toString());
					}
				}
			}
			auditLogContext.putValue(paramBuilder.build());
		} catch (Exception ex) {
			log.error("", ex);
		}
		return joinPoint.proceed();
	}

	@Pointcut("@annotation(com.adan.auditlog.annotation.MyBatisAuditLog)")
	private void mapper() {
	}
}
